/*+***********************************************************************************
 Filename: 9k_mcu01_mycore_v01\src\perips\zh_timer_v01.v
 Description: a simple timer peripheral module
    with  4 I/O registers: ctrl, counter, top, overflow status.

 Modification:
   2024.02.09 Creation   H.Zheng  (A01_tinyriscv_mcu01\src\perips\timer.v)
   2024.05.23 seperate timeout status from ctrl reg
              add ce_i port  
   2025.08.17 rearrange to 9k_mcu01_mycore_v01 project
              change addr to 6bit width

Copyright (C) 2024  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/
/**
 * I/O Regs:
 *  offset      reg
 * 0x00         timer_ctrl,bit 0: enable
 * 0x04         counter  
 * 0x08         counter_top
 * 0x0c         overflow status(bit0), clear when read, clear when timer disable
 */
module zh_timer_v01(

    input wire clk,
    input wire reset_n,

    input wire[31:0] data_i,
    input wire[5:0] addr_i,
    input wire we_i,
    input wire ce_i,

    output wire[31:0] data_o,
    output wire int_sig_o

    );

    reg [31:0] timer_ctrl; //bit 0: enable
    reg overflow_status; // overflow status
    reg [31:0] counter;
    reg [31:0] counter_top;

    wire write_en = ce_i & we_i;
    wire read_en = ce_i & (~we_i);

    /**
     * counter logic
     */
    always @(posedge clk) begin
        if (reset_n == 1'b0) begin
            counter <= 32'b0;
        end
        else if (write_en &&(addr_i == 6'h1)) begin //load counter initial value
            counter <= data_i;
        end 
        else begin
            if (counter >= counter_top) begin //overflow
                counter <= 32'b0;
            end
            else if (timer_ctrl[0]) begin  //tick counter
                counter <= counter + 1;
            end
        end
    end

    /**
     * ctrl logic
     */
    always @(posedge clk) begin
        if (reset_n == 1'b0) begin
            timer_ctrl <= 32'b0;
        end
        else if (write_en &&(addr_i == 6'h0)) begin //set ctrl reg
            timer_ctrl <= data_i;
        end
    end

    /**
     * counter_top logic
     */
    always @(posedge clk) begin
        if (reset_n == 1'b0) begin
            counter_top <= ~32'b0;
        end
        else if (write_en &&(addr_i == 6'h2)) begin //set counter_top reg
            counter_top <= data_i;
        end 
    end
    /**
     * overflow_status logic
     */
    //auto clear while counter disable or overflow_status is read
    always @(posedge clk) begin
        if (reset_n == 1'b0) begin
            overflow_status <= 1'b0;
        end
        else if ((counter >= counter_top) && timer_ctrl[0]) begin //overflow
            overflow_status <= 1'b1;
        end
        else if (~timer_ctrl[0]) begin //clear while counter disable
            overflow_status <= 1'b0;
        end 
        else if (read_en &&(addr_i == 6'h3)) begin //clear while read overflow status
            overflow_status <= 1'b0;
        end 
    end

    assign int_sig_o = overflow_status;

    //log overflow_status to overflow_status_reg
    reg overflow_status_reg;
    always @(posedge clk) begin
        overflow_status_reg <= overflow_status;
    end


    assign data_o = (addr_i == 6'h0) ? timer_ctrl :
                    (addr_i == 6'h1) ? counter : 
                    (addr_i == 6'h2) ? counter_top :
                    (addr_i == 6'h3) ? {31'b0, overflow_status_reg}  : ~0;



endmodule
